namespace Bread.IoC;

public interface IResolver : IDisposable
{
    object Resolve();

    object Resolve(IOverrides overrides);
}

public interface ICreator<T>
{
    T Create();

    T Create(IOverrides overrides);
}

public sealed class SingletonResolver<TCreator, TType> : IResolver where TCreator : struct, ICreator<TType>
{
    private object @object = new object();
    private object? _cache;
    private bool _disposed;
    private Func<object> _resolve;
    private Func<IOverrides, object> _resolveOverride;

    public SingletonResolver()
    {
        _cache = null;
        _disposed = false;

        _resolve = ResolveInternal;
        _resolveOverride = ResolveInternalOverride;
    }

    public object Resolve()
    {
        return _resolve();
    }

    public object Resolve(IOverrides overrides)
    {
        return _resolveOverride(overrides);
    }

    public void Dispose()
    {
        if (_disposed) {
            throw new ObjectDisposedException("Instance resolver was disposed. It may happen because the scope was disposed.");
        }

        if (_cache != null && _cache is IDisposable disposable) {
            _disposed = true;
            disposable.Dispose();
        }
    }

    private object ResolveInternal()
    {
        lock (@object) {
            if (_cache != null) {
                return _cache;
            }

            var creator = default(TCreator);
            _cache = creator.Create();
            _resolve = () => _cache;
            _resolveOverride = (oo) => _cache;
            return _cache;
        }
    }

    private object ResolveInternalOverride(IOverrides overrides)
    {
        lock (@object) {
            if (_cache != null) {
                return _cache;
            }

            var creator = default(TCreator);
            _cache = creator.Create(overrides);
            _resolve = () => _cache;
            _resolveOverride = (oo) => _cache; ;
            return _cache;
        }
    }
}

public sealed class SingletonResolver : IResolver
{
    private readonly Func<object> _activator;
    private object _cache;
    private bool _disposed;
    private Func<object> _resolve;

    public SingletonResolver(Func<object> activator)
    {
        _activator = activator;
        _cache = null;
        _disposed = false;

        _resolve = ResolveInternal;
    }

    public object Resolve()
    {
        return _resolve();
    }

    public object Resolve(IOverrides overrides)
    {
        return _resolve();
    }

    public IResolver Duplicate()
    {
        return new SingletonResolver(_activator);
    }

    public void Dispose()
    {
        if (_disposed) {
            throw new ObjectDisposedException("Instance resolver was disposed. It may happen because the scope was disposed.");
        }

        if (_cache != null && _cache is IDisposable disposable) {
            _disposed = true;
            disposable.Dispose();
        }
    }

    private object ResolveInternal()
    {
        lock (_activator) {
            if (_cache != null) {
                return _cache;
            }

            _cache = _activator();
            _resolve = GetCached;
            return _cache;
        }
    }

    private object GetCached()
    {
        return _cache;
    }
}